#include "../include/QDessinGL.h"
#include "../include/Image.h"
#include "../include/Masque.h"
#include "../include/Pixel.h"
#include "../include/InpaintingThese.h"
#include "QGLView.h"
#include <iostream>
#include <list>

QDessinGL::QDessinGL(QString t_nomFichier,QWidget *parent):QGLWidget(parent){
    nomFichier=t_nomFichier;
    im = NULL;
    u = NULL;
    u1 = NULL;
    laplacien = NULL;
    laplacienU = NULL;
	gradientX = NULL;
	gradientY = NULL;
    m = NULL;
    MousePrevX = 100;
    MousePrevY = 100;
    setMouseTracking(true);
    setFocusPolicy(Qt::StrongFocus);
    deltat = 0.5;
    lambda = 0.2;
    nbIter = 1000;
    previsu = false;
    algoLaunched = false;
    zoom = 1.;
    xImg = 0;
    yImg = 0;
	deltat2=0.1;
	deltat3=0.2;
	lambda2=10;
	algo = 1;
	sigma = 1.0;
	deltat4 = 1.0;
	Nvecteurs = false;
	randMask = 0;
}
QDessinGL::~QDessinGL(){}
void QDessinGL::setFichier(QString t_fichier)
{
    if(!t_fichier.isEmpty())
    {
        nomFichier=t_fichier;
        delete im;
        delete m;

        im = new Image();

        im->chargePPM(t_fichier.toAscii());
        im->toSprite();

        this->setFixedSize(im->getL(),im->getH());
        m = new Masque(im->getL(),im->getH());
		algoLaunched = false;
    }
}

void QDessinGL::setMasque(QString t_fichier){
	if(!t_fichier.isEmpty())
	{
		delete m;
		laplacienU = new Image();
		laplacienU->chargePPM(t_fichier.toAscii());
		m = new Masque(laplacienU->getL(),laplacienU->getH());
		m->getMaskFromPicture(laplacienU,Pixel(255,0,0));
		delete laplacienU;

	}
}

void QDessinGL::paintGL()
{
    glClear (GL_COLOR_BUFFER_BIT);
    glPixelZoom(zoom,zoom);
    glRasterPos2i(0,0);
    glBitmap(0,0,0,0,xImg,yImg,NULL);
    if(!algoLaunched){
		im->Afficher();
		m->Afficher();
		m->AffichePinceau(MousePrevX,MousePrevY);
    }
    else
		u1->Afficher();

}
void QDessinGL::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m->Dessine(event->x(),im->getH()-event->y(),MousePrevX,MousePrevY,255);
    }
    if (event->button() == Qt::RightButton) {
        m->Dessine(event->x(),im->getH()-event->y(),MousePrevX,MousePrevY,0);
    }
    this->updateGL();
}

void QDessinGL::mouseMoveEvent(QMouseEvent *event)
{
    if ((event->buttons() & Qt::LeftButton))
        m->Dessine(event->x(),im->getH()-event->y(),MousePrevX,MousePrevY,255);
    if ((event->buttons() & Qt::RightButton))
        m->Dessine(event->x(),im->getH()-event->y(),MousePrevX,MousePrevY,0);
    MousePrevX = event->x();
    MousePrevY = im->getH()-event->y();
    this->updateGL();
}

void QDessinGL::wheelEvent(QWheelEvent *event){
   zoom += (double)event->delta()/1000;
   if(zoom<1)
       zoom = 1;
   //m->setZoom(zoom);
   xImg = (-event->x())*(zoom-1);
   yImg = (event->y()-im->getH())*(zoom-1);
   this->updateGL();
}

void QDessinGL::keyPressEvent(QKeyEvent * event){
    switch (event->key()) {
        case Qt::Key_E:
            m->Effacer();
            break;

    }
    this->updateGL();
}

void QDessinGL::initializeGL()
{
    glPixelStorei(GL_UNPACK_ALIGNMENT,1);
    glEnable(GL_BLEND);
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    this->setCursor(QCursor( Qt::BlankCursor ));
}

void QDessinGL::resizeGL(int w,int h)
{
    glViewport(0, 0, w, h) ;
    glMatrixMode(GL_PROJECTION) ;
    glLoadIdentity() ;
    gluOrtho2D(0, w, 0, h) ;
    glMatrixMode(GL_MODELVIEW) ;
    glLoadIdentity() ;
} 

void QDessinGL::setTaillePinceau(int taille){
    m->setTaillePinceau(taille);
}
void QDessinGL::setLambda(double val){
    lambda = val;
}
void QDessinGL::setLambda2(double val){
	lambda2 = val;
}
void QDessinGL::setDeltat(double val){
    deltat = val;
}
void QDessinGL::setDeltat2(double val){
	deltat2 = val;
}
void QDessinGL::setDeltat3(double val){
	deltat3 = val;
}
void QDessinGL::setSigma(double val){
	sigma = val;
}
void QDessinGL::setDeltat4(double val){
	deltat4 = val;
}

void QDessinGL::setNvecteurs(bool val){
	Nvecteurs = val;
}

void QDessinGL::setRandMask(int val){
	randMask = val;
}


void QDessinGL::setAlgo(int val){
	std::cout<<val<<std::endl;
	algo = val;
}


void QDessinGL::setNbIter(int val){
    nbIter = val;
}

void QDessinGL::setPrevisu(bool val){
    previsu = val;
}

void QDessinGL::algoSimple(){
    algoLaunched = true;
    this->setFocus();
	laplacien = im->Laplacien();
    u1 = im->CopierImage();
    for(int i = 1;i <=nbIter;i++){
		emit nbItPr((i*100)/nbIter);
	    delete u;
		    u = u1->CopierImage();
	    delete laplacienU;
	    laplacienU = u->Laplacien();
	    delete u1;
	    u1 = new Image(im->getL(),im->getH());

		for(int y=0;y<im->getH();y++){

			for(int x=0;x<im->getL();x++){
				int R = 1-m->LitBool(x,im->getH()-y);//hauteur-y car les images sont stocké a l'envers du masque
				Pixel fp = im->LitPixel(x,y);
				if(!R){

					Pixel up = u->LitPixel(x,y);
					Pixel ulp = laplacienU->LitPixel(x,y);
					Pixel tmp = (ulp*lambda + (fp-up)*(2.*R))*deltat+up;
					u1->EcritPixel(x,y,tmp);
				}
				else
					u1->EcritPixel(x,y,fp);
		    }
	    }
	    if(previsu){
			glClear (GL_COLOR_BUFFER_BIT);
			u1->toSprite();
			u1->Afficher();
			updateGL();
	    }
		//((QGLView*)parentWidget())->getTools()->repaint();
    }
    u1->toSprite();
	u1->sauvePPM("resultat.ppm");
    updateGL();
	emit nbItPr(100);
}

double min(double a){
	if ( a < 0 )
		return a;
	return 0;
}

double max(double a){
	if ( a > 0 )
		return a;
	return 0;
}

void QDessinGL::algoPapier(){
	double dt=deltat2;

	algoLaunched = true;
	this->setFocus();
	//unused var//bool up=true;
	Image* swap;
	u1 = im->CopierImage();
	u = im->CopierImage();
	//m->Traitement();
	m->empiler();
	m->dilatation(1);
	m->empiler(true);

	std::list<Point>::iterator it;
	laplacien = new Image(im->getL(),im->getH());
	gradientX= new Image(im->getL(),im->getH());
	gradientY= new Image(im->getL(),im->getH());

	/*for(it = m->listePos.begin();it!=m->listePos.end();it++){
		u1->EcritPixel((*it).x,im->getH()-(*it).y,Pixel(0,0,0));
		u->EcritPixel((*it).x,im->getH()-(*it).y,Pixel(0,0,0));
	}*/

	for(int nbIt = 0;nbIt <this->nbIter;nbIt++){
		emit nbItPr((nbIt*100)/nbIter);


		if(nbIt%15==0){
			Image* tmp = u1;
			u1 = u1->DiffusionAnisotrope(m);
			delete tmp;

		}
		/*if(nbIter%5==0&&up){
			up = m->Update();
			m->empiler();
			//dt*=0.9;
			cout<<dt<<endl;
		}*/
		swap=u1;
		u1 = u;
		u = swap;

		for(it = m->listePosDilat.begin();it!=m->listePosDilat.end();it++){
			int x = (*it).x;
			int y = im->getH() -(*it).y;
			laplacien->EcritPixel(x,y,u->Laplacien(x,y));
			gradientX->EcritPixel(x,y,u->Gradientx(x,y));
			gradientY->EcritPixel(x,y,u->Gradienty(x,y));
		}
		for(it = m->listePos.begin();it!=m->listePos.end();it++){

			int x = (*it).x;
			int y = im->getH() -(*it).y;
			for(int nbMiniIter = 0;nbMiniIter<1;nbMiniIter++){

				vecteur* isophote = u->computeIsophoteDirection(x,y,gradientX,gradientY);
				vecteur* smooth = u->ComputeSmoothness(x,y,laplacien);

				double r=1,v=1,b=1;

				r *= smooth[0].x*isophote[0].x+smooth[0].y*isophote[0].y;
				v *= smooth[1].x*isophote[1].x+smooth[1].y*isophote[1].y;
				b *= smooth[2].x*isophote[2].x+smooth[2].y*isophote[2].y;

				Pixel CC = (*u)(x,y);
				Pixel PC = (*u)(x-1,y);
				Pixel NC = (*u)(x+1,y);
				Pixel CP = (*u)(x,y-1);
				Pixel CN = (*u)(x,y+1);

				double rxb = CC[0]-PC[0];
				double rxf = NC[0]-CC[0];
				double ryb = CC[0]-CP[0];
				double ryf = CN[0]-CC[0];


				double vxb = CC[1]-PC[1];
				double vxf = NC[1]-CC[1];
				double vyb = CC[1]-CP[1];
				double vyf = CN[1]-CC[1];


				double bxb = CC[2]-PC[2];
				double bxf = NC[2]-CC[2];
				double byb = CC[2]-CP[2];
				double byf = CN[2]-CC[2];

				if(r>0)
					r*=sqrt(min(rxb)*min(rxb)+max(rxf)*max(rxf)+min(ryb)*min(ryb)+max(ryf)*max(ryf));
				else if (r<0)
					r*=sqrt(max(rxb)*max(rxb)+min(rxf)*min(rxf)+max(ryb)*max(ryb)+min(ryf)*min(ryf));

				if(v>0)
					v*=sqrt(min(vxb)*min(vxb)+max(vxf)*max(vxf)+min(vyb)*min(vyb)+max(vyf)*max(vyf));
				else if (v<0)
					v*=sqrt(max(vxb)*max(vxb)+min(vxf)*min(vxf)+max(vyb)*max(vyb)+min(vyf)*min(vyf));

				if(b>0)
					b*=sqrt(min(bxb)*min(bxb)+max(bxf)*max(bxf)+min(byb)*min(byb)+max(byf)*max(byf));
				else if (b<0)
					b*=sqrt(max(bxb)*max(bxb)+min(bxf)*min(bxf)+max(byb)*max(byb)+min(byf)*min(byf));


				r = CC[0] + dt*r;
				v = CC[1] + dt*v;
				b = CC[2] + dt*b;
				Pixel tmp(r,v,b);
				delete []isophote;
				delete []smooth;


				u1->EcritPixel(x,y,tmp);

			}

		}
		if(previsu){
			glClear (GL_COLOR_BUFFER_BIT);
			u1->toSprite();
			u1->Afficher();
			updateGL();
		}
		//((QGLView*)parentWidget())->repaint();

	}
	delete laplacien;
	delete gradientX;
	delete gradientY;
	u1->sauvePPM("resultat.ppm");
	swap = im;
	im = u1;
	delete swap;
	delete m;
	m = new Masque(im->getL(),im->getH());
	algoLaunched = false;
	emit nbItPr(100);
}

void QDessinGL::Lauch(){
	if(algo==0)
		algoSimple();
	else if(algo==1)
		algoPapier();
	else if(algo==2){
		u1 = new Image(im->getL(),im->getH());
		inp = new InpaintingThese(im,3,m,u1,sigma,20,this);

		inp->tout ();
	}
}

bool QDessinGL::getPrevisu(){
	return previsu;
}
